This is a demonstration of how the process might work by hand.

Basic Steps, Illustrated

Crop out ruler

Filter out background noise

Clean up the image

Separate regions and identify the centers

Measure distance from point to point to create a mesh

Measurement is obviously trickier but I used the ruler tool in GiMP to show the process.

Basic Algorithm for ECCO shoes

## Loading required package: magrittr
## 
## Attaching package: 'imager'
## The following object is masked from 'package:magrittr':
## 
##     add
## The following objects are masked from 'package:stats':
## 
##     convolve, spectrum
## The following object is masked from 'package:graphics':
## 
##     frame
## The following object is masked from 'package:base':
## 
##     save.image
## ── Attaching packages ─────────────────────────────────────── tidyverse 1.3.0 ──
## ✓ ggplot2 3.3.3     ✓ purrr   0.3.4
## ✓ tibble  3.0.5     ✓ dplyr   1.0.3
## ✓ tidyr   1.1.2     ✓ stringr 1.4.0
## ✓ readr   1.4.0     ✓ forcats 0.5.0
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## x imager::add()       masks magrittr::add()
## x stringr::boundary() masks imager::boundary()
## x tidyr::extract()    masks magrittr::extract()
## x tidyr::fill()       masks imager::fill()
## x dplyr::filter()     masks stats::filter()
## x dplyr::lag()        masks stats::lag()
## x purrr::set_names()  masks magrittr::set_names()

Crop and Filter

# Remove EverOS ruler
crop_everos <- function(im) {
  # Cut out yellow/red area
  img_bbox <- im %>%
    imsplit(axis = "c") %>%
    (function(x) is.finite((x[[1]] + x[[2]])/x[[3]])) %>%
    as.cimg() %>%
    (function(x) x == 1)
  
  crop.bbox(im, img_bbox) %>%
    # convert to grayscale
    grayscale() %>%
    # Autocrop each half of the image
    ImageAlignR::map_halfimg(fun = autocrop) %>%
    # Crop 5px from each side to clean residual bits up
    crop.borders(nx = 5, ny = 5) %>%
    # Crop out extra white space
    autocrop() %>%
    # Convert to black and white
    threshold() %>%
    # Clean up fuzz (at most 3x3 px)
    shrink(3) %>%
    grow(3) %>%
    # Crop again
    autocrop()}

Clean Image

Cleaning the image requires that we have a minimum “interesting” size and shape - this is used to define a filter. Many of these filters work better on an inverted image.

Measuring the dots in the ECCO shoes in an image editing program, we can see that the small dots are ~ 25 px across. Our filter, thus, is a circular matrix with radius 10 pixels - smaller than the small dots we want to keep.

### Check basic shape statistics

This portion of the algorithm explicitly depends on the size and shape of the dominant feature in the shoe. It would have to be modified to work with e.g. hexagons or rectangles, and would still only work for a regular pattern of the same shape.

As part of the shape filtering, we calculate the center (median) of each shape. This obviously doesn’t work properly for concave shapes, but for circles it is just fine.

Create mesh from centers

## 
## Attaching package: 'tripack'
## The following object is masked from 'package:imager':
## 
##     circles